//////////////////////////////////////////////////////////////////////////////
//
// File:  conGuy.cc
//
// Description:  This is the implementation file for the conGuy class.
//   This class manages and processes all the necessary actions
//   required to connect to bnet.  (accnt, pwd and auth challenges).
//   It also handles all the interface and packet communication to bnet
//   after the connect.
// 
// Status:  Complete
//
// Notes:  
//
//   1.  I have "exeVer = 0x01010101; " hardcoded because I haven't
//   looked into wine to find the translated linux equivalent of the
//   windows API that extracts dll version info.  (search for 'exeVer').
//   
//   UPDATE: After rigoriously searching through the pages and pages of
//   wine code, I found out that wine does not have the code to extract
//   the exe version.
//
//   Note, the way it's stored here is in reverse order of how it's
//   logged in the packet logs. (due to endianess)
// 
//   2.  I have u32 'versionByte' hardcoded in function
//   bool conGuy::s_pkt_SID_AUTH_INFO().  This has been noted to
//   change less often from version to version.  It probably won't
//   anymore, but if it does, that's where it is located.
//
//   3.  As a reference to anyone trying to figure out how to make
//   this work in the future, here's the information on the login
//   packet sequence.  You will need to get a packet logger (such as
//   ethereal) and log the packets from a real Starcraft Battle.net
//   client.  Then look at the packet logs.  This is the login
//   sequence: (In parentheses is the packet ID in the form 0xHH).
//
// SEND -> Protocol byte(01)
// SEND ->SID_AUTH_INFO(0x50)
// RECV <-SID_PING(0x25)
// RECV <-SID_AUTH_INFO(0x50)
// SEND ->SID_PING(0x25) [Optional]
// SEND ->SID_AUTH_CHECK(0x51)
// RECV <-SID_AUTH_CHECK(0x51)
// SEND ->SID_LOGONRESPONSE(0x29)
// RECV <-SID_LOGONRESPONSE(0x29)
// SEND ->SID_UDPPINGRESPONSE(0x14) [SEXP/STAR/W2BN]
// SEND ->SID_ENTERCHAT(0x0A)
//
// That being said, here's the info on the packet headers and how to
// tell the difference between different packets.  Each packet start
// like this:
//
// (BYTE)      Always 0xFF
// (BYTE)      Message ID
// (WORD)      Message length, including this header (2 bytes)
// (VOID)      Message Data
// 
// BYTE  == 0xHH
// WORD  == 0xHH 0xHH
// DWORD == 0xHH 0xHH 0xHH 0xHH
//
///////////////////////////////////////////////////////////////
//
// exeVer is located in (SEND->SID_AUTH_CHECK(0x51)), and it's the
// 9th-12th bytes.  ie. if the packet looks like this: 
//
// 0xHH 0xHH 0xHH 0xHH 0xHH 0xHH 0xHH 0xHH 0x** 0x** 0x** 0x** 
//                                         ^ 
//                                         ^ 
//                                         ^ is where the exeVer is.
//
// 1 byte == 0xHH
//
// Also, note that the way it's stored here is in reverse order of how
// it's logged in the packet logs. (due to endianess).  So you need to
// take every two hex values and flip them.  For example:
//
// 0x12 0x34 0x45 0x67  
// in the packet log becomes -->  0x67 0x45 0x34 0x12
//
//////////////////////////////////////////////////////////////
//
// u32 versionByte is located in the SEND->SID_AUTH_INFO(0x50) packet.
// It is the 5th dword, or the 18-21st bytes.
//
/////////////////////////////////////////////////////////////////////
//
#include <iostream>
#include <string>
#include <fstream>
#include <ctime>
#include <assert.h>

// For sockets:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <errno.h>

// For anti-flood
#include <sys/time.h>

using namespace std;

// My stuff:
#include "conGuy.h"
#include "buff.h"
#include "../parser/parser.h"
#include "types.h"
#include "../event/event.h"


// My #define's and global externs:

// conGuys' extern ofstream *debug shouldn't clobber each other 
// since most of the output to these ofstreams are at the connect, 
// done asynchronously.  If it becomes a problem
// I can change these to be not externed and be pointing to another
// file ofstream opened at the constructor.
extern ofstream *debug;
//extern ofstream *gitmlog;

// This is the default maximum recv message size.
#define MAX_MSG 1024  

// Default connect config file
#define CONGUY_CONFIG_FILE "config/connect.cfg"  

// This is the number of seconds between the
// last send time and the time a SID_NULL packet will need to be sent.
#define SECONDS_SID_NULL 480 



//////////////////////////////////////////////////////////////////////////////
//
// Function:  conGuy::conGuy() 
//
// Description:
//   This is the conGuy default constructor.  Initializes default values
//   and inits the pthread mutexes.
//
conGuy::conGuy() {
  int rc;
  port = -1;
  sd = -1;
  done = false;

  // Initialize time as 0;  (This is for the flood protection)
  lastSendTime.tv_sec = 0;
  lastSendTime.tv_usec = 0;
  lastSendSize = 0;

  // Turn off flood control untill we're logged in.
  sleep_sec_req = 0;        // Minimum required seconds to sleep
  sleep_usec_req = 0;       // Minimum required microseconds to sleep.
  sleep_usec_per_byte = 0;  // Number of microseconds to sleep per byte
                            //   when packet size is > 30 bytes.

  // Initialize start time to zero untill we actually connect.
  startTime = 0;
  
  // Initialize the sendQueue toSend mutex.
  rc = pthread_mutex_init(&(toSend.mutex), NULL);
  rc = pthread_mutex_init(&(requests.mutex), NULL);
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  conGuy::~conGuy()
//
// Description:  
//   This is the conGuy destructor.  It disconnects the socket if it is open
//   and destroys all mutexes.
//
conGuy::~conGuy() {
  int rc;
  if(sd != -1) {
    // Socket is still open so close it.
    disconnectNow();
  }
  // Get a lock on the toSend mutex and requests mutex and destroy them.
  rc = pthread_mutex_lock(&(toSend.mutex));
  rc = pthread_mutex_destroy(&(toSend.mutex));
  rc = pthread_mutex_lock(&(requests.mutex));
  rc = pthread_mutex_destroy(&(requests.mutex));
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::connectNow(string this_gateway)
//
// Description:  
//   Read the config file in and do the essential stuff to connect to bnet.
// 
//
bool conGuy::connectNow(string this_gateway) {
  gateway = this_gateway;
  done = false;

  if(isConnected() == true)
    disconnectNow();
  
  string logfilename = string(LOGFILE_BASE) + 
                       string("connection.log.") + 
                       this_gateway;
  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  // Close the conlog if it is open right now.
  if(conlog.is_open()) {
    conlog.close();
  }

  // Open the logfile for appending.
  conlog.open(logfilename.c_str(),
	       ios_base::out | ios_base::app);

  // Log timestamp of connection start.
  conlog << endl
	  << "---------------------------------------------------------------"
	  << endl << "GiTMbot connecting at:                 "
	  << string(asctime(timeinfo))
	  << "---------------------------------------------------------------"
	  << endl;
  
  conlog << "Reading in connection configuration file " 
       << string(CONGUY_CONFIG_FILE)
       << " ..." << endl;
  if(!read_config(this_gateway)) {
    *debug << "ERROR in read_config call" << endl;
    return false;
  }
  conlog << "Connecting to server " << server << " on port "
	 << short(port) << " ..." << endl;

  
  if(!connectToBnet()) {
    *debug << "ERROR in connectToBnet call" << endl;
    disconnectNow();
    return false;
  }
  conlog << "Server logon successful.   :)" << endl;
  time( &startTime);
  *debug << "DEBUG:  Got through the readconfig and connectToBnet! Woot!"
	 << endl;

  // Turn on flood control.  (set the parameters).
  sleep_sec_req = 0;            // Minimum required seconds to sleep
  sleep_usec_req = 1220000;     // Minimum required microseconds to sleep.
  sleep_usec_per_byte = 43000;  // Number of microseconds to sleep per byte
                                // when packet size is > 30 bytes.

  return true;
}  // end of bool conGuy::connectNow()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::disconnectNow()
//
// Description: 
//   This function closes the socket if it is open.
//
bool conGuy::disconnectNow() {
  // If the socket discriptor isn't -1, (means socket is still open), close it.
  *debug << "DEBUG:  disconnectNow()" << endl;
  conlog << "Disconnecting from the server." << endl;
  if(sd != -1) {
    // Send the leavechat packet and close the socket.
//     if(s_pkt_SID_LEAVECHAT())
//       *debug << "DEBUG:  just sent SID_LEAVECHAT" << endl;
    close(sd);
  }
  sd = -1;
  // Reset the start time to 0.
  startTime = 0;



  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  conlog << "GiTMbot disconnect at:                 "
	  << string(asctime(timeinfo))
	  << "---------------------------------------------------------------"
	  << endl;

  conlog.close();


  // Turn off flood control.  (We'll turn it back on when we're connected).
  sleep_sec_req = 0;        // Minimum required seconds to sleep
  sleep_usec_req = 0;       // Minimum required microseconds to sleep.
  sleep_usec_per_byte = 0;  // Number of microseconds to sleep per byte
                            //   when packet size is > 30 bytes.

  return true;
}  // end of    bool conGuy::disconnectNow()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::isConnected()
//
// Description:
//   This function returns if the socket is valid or not.  (connected).
//
bool conGuy::isConnected() {
  if(sd != -1)
    return true;
  else 
    return false;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  string conGuy::getMainGateway()
//
// Description: 
//   This returns the gateway that the main bot will run on.  (The one
//   with the sql database).
//
string conGuy::getMainGateway() {
  return bot_gateway;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  string conGuy::getGateway()
//
// Description:
//   This function returns the gateway that the conGuy is connected to.
//
string conGuy::getGateway() {
  return gateway;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  sendQueue* conGuy::getSendQueue()
//
// Description:
//   Return the pointer to the toSend sendQueue.  This 'sendQueue'  is a queue
//   of messages that need to be sent out.  Remember!  The sendQueue
//   has a pthread_mutex!  Lock before modifying.  :)
//
sendQueue* conGuy::getSendQueue() {
  return &toSend;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  sendRequests* conGuy::getSendRequests()
//
// Description:
//   Return the pointer to the requests sendRequests.  Remember!  The 
//   sendRequests object has a pthread_mutex!  Lock before modifying.
//
sendRequests* conGuy::getSendRequests() {
  return &requests;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::purgeQueues(); 
//
// Description:
//   This function purges the sendQueue and the sendRequests.
//
//   If there is any sends in our sendQueue we want to pop them all off.
//   If there are any pending requests from other conGuys, we 
//   should at least alert to the requesting conGuy (if he's still connected)
//   that we are disconnected.

bool conGuy::purgeQueues() {
  int rc;

  // Check if they are already purged.
  if(toSend.out.size() == 0 && requests.out.size() == 0)
    return true;

  // Purge our sendQueue.
  rc = pthread_mutex_lock(&(toSend.mutex));
  while(toSend.out.size() > 0)
    toSend.out.pop_front();
  rc = pthread_mutex_unlock(&(toSend.mutex));

  // Purge our sendRequests.
  rc = pthread_mutex_lock(&(requests.mutex));
  while(requests.out.size() > 0) {
    string msg         = requests.out.front();
    conGuy *comingFrom = requests.fromConGuy.front();
    string fromUser    = requests.fromWho.front();
    requests.out.pop_front();
    requests.fromConGuy.pop_front();
    requests.fromWho.pop_front();

    if(comingFrom->isConnected() == true) {
      // Return a message that says the bot is disconnected.
      sendQueue *toSendTheirs = comingFrom->getSendQueue();
      rc = pthread_mutex_lock(&(toSendTheirs->mutex));
      toSendTheirs->out.push_back( string("/w ") + fromUser + string(" ") +
				   getGateway() + 
				   string(" bot is disconnected."));
      rc = pthread_mutex_unlock(&(toSendTheirs->mutex));
    }
    else {
      // So the guy we were servicing a request for isn't connected.  Oh well.
      // I suppose we'll continue on since we've popped already...
    }
  }
  rc = pthread_mutex_unlock(&(requests.mutex));
  // All done purging!
  return true;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  int conGuy::getSd()
//
// Description:
//   Return the socket descriptor.  This could be useful I think.
//
int conGuy::getSd() {
  return sd;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  string conGuy::homeChannel()
//
// Description:
//   Returns the home_channel designated in the config file.  
//
string conGuy::homeChannel() {
  return home_channel;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  string conGuy::username()
//
// Description:
//   Returns the unique username designated at login time.  
//
string conGuy::username() {
  return unique_username;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::getHaveOps()
// 
// Description:
//   Accessor function that lets us know if we have channel ops or not.
//
bool conGuy::getHaveOps() {
  return haveOps;
}


//////////////////////////////////////////////////////////////////////////////
// 
// Function:  bool conGuy::setHaveOps(bool ops)
//
// Description:
//   Sets the haveOps flag.
//
bool conGuy::setHaveOps(bool ops) {
  haveOps = ops;
  return true;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  time_t conGuy::getStartTime() 
//
// Description:
//   Accessor function that gives us the time_t startTime that it takes the 
//   timestamp of when it connects.
//
time_t conGuy::getStartTime() {
  return startTime;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::say(string msg)
//
// Description:
//   Send bnet a chat message or command.
// 
bool conGuy::say(string msg) {
  return s_pkt_SID_CHATCOMMAND(msg);
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::read_config(string this_gateway)
//
// Description:
//   This function reads the #defined CONGUY_CONFIG file for the configuration
//   stuff.
//
bool conGuy::read_config(string this_gateway) {

  // Parse dat shizznitz!
  parser parserGuy(string(CONGUY_CONFIG_FILE));

  string hash1;   // hash file filenames
  string hash2;
  string hash3;
  string password;
  string cdkey;
  // Fetch them bones.

  // Fetch the gateway that the ACTUAL bot will run on.  (not necessarily this
  //   conGuy).
  if(!parserGuy.fetch(string("gateway"), bot_gateway)) {
    *debug << "ERROR: Couldn't get gateway from parser.  (check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get gateway from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  *debug << "DEBUG:  Read in from config file gateway: " << bot_gateway << endl;
  
  
  // Fetch server.
  if(!parserGuy.fetch( string("server_") + this_gateway, server)) {
    *debug << "ERROR: Couldn't get server from parser.  (check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get server from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  *debug << "DEBUG:  Read in from config file server: " << server << endl;

  // Fetch port #.
  string portStr;
  if(!parserGuy.fetch(string("port"), portStr)) {
    *debug << "ERROR: Couldn't get port # from parser.  (check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get port # from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  // Convert from string to int.
  port = atoi(portStr.c_str());
  
  // Fetch home channel.
  if(!parserGuy.fetch(string("home_channel_") + this_gateway, home_channel)) {
    *debug << "ERROR: Couldn't get home_channel from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get home_channel from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }

  // Fetch user accnt name.
  if(!parserGuy.fetch(string("user_") + this_gateway, account)) {
    *debug << "ERROR: Couldn't get user accnt from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get user account from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  
  // Fetch password.
  if(!parserGuy.fetch(string("password_") + this_gateway, password)) {
    *debug << "ERROR: Couldn't get password from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get password from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  
  // Fetch cdkey.
  if(!parserGuy.fetch(string("cdkey"), cdkey)) {
    *debug << "ERROR: Couldn't get cdkey from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get cdkey from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }

  // Fetch hash1
  if(!parserGuy.fetch(string("hash1"), hash1)) {
    *debug << "ERROR: Couldn't get hash1 from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get hash1 filename from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  // Fetch hash2
  if(!parserGuy.fetch(string("hash2"), hash2)) {
    *debug << "ERROR: Couldn't get hash2 from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get hash2 filename from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  // Fetch hash3
  if(!parserGuy.fetch(string("hash3"), hash3)) {
    *debug << "ERROR: Couldn't get hash3 from parser.(check cfg file?)"
	   << endl << "         " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    conlog << "ERROR: Couldn't get hash3 filename from config file."
	     << "  (check connect.cfg file?)" << endl;
    return false;
  }
  
  // Tell authGuy what those values are.
  auth.setAccount(account);
  auth.setPassword(password);
  auth.setCdkey(cdkey);
  auth.setHashfile1(hash1);
  auth.setHashfile2(hash2);
  auth.setHashfile3(hash3);
  
  
  
  *debug << "DEBUG: Finished extracting data needed from conGuy config"<< endl;
  
  return true;
} // end of bool conGuy::read_config()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  int conGuy::readit(int socket, void*msg_data, 
//                               unsigned int msg_len)
//
// Description:  
//   Reads data from a socket.  msg_len is max message size.  The return value
//   is how many bytes were read.
//
// TODO:  Look over this function and re-comment the description to this function if need be.
//
//
int conGuy::readit(int socket, void*msg_data, unsigned int msg_len) {
  char *buf_ptr;
  int   in = 0;

  buf_ptr = (char*)msg_data;
  /* Handle possible interrupt before entire buffer is read in. */
  in = 0;
  //  while( msg_len > 0 && in != -1) {
  // *debug << "DEBUG:  in readit():  while loop 1" << endl;
    /* Handle the actual interrupt of the read by repeating
     * the read until we have no EINTR 'error'
     */
  in = read(socket, buf_ptr, msg_len);
  while(in == -1 && errno == EINTR) {
    in = read(socket, buf_ptr, msg_len);
  }
    /* Here we handle the possibility of reading less
     * than the entire buffer due to an interrupt.
     */
    
    //    *debug << "DEBUG:  readit():  in == " << in << endl;
    /*
    if( in > 0 ) {
      msg_len -= in;
      buf_ptr += in;
    }
    else*/ if (in < 0) {
      *debug << "ERROR:  readit()" << endl
	     << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
      conlog << "ERROR:  Socket read error." << endl;
	     }
    //  }
  return in;
} // end of int conGuy::readit()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  int conGuy::writeit(int socket, void*msg_data, 
//                                unsigned int msg_len)
// 
// Description:
//   Writes data to a socket descriptor or length msg_len.
//
int conGuy::writeit(int socket, void*msg_data, unsigned int msg_len) {
  char *buf_ptr;
  int   sent = 0;

  buf_ptr = (char*)msg_data;
  /* Handle possible interrupt before entire buffer
   * is written out.
   */
  sent = 0;
  while( msg_len > 0 && sent != -1) {
    /* Handle the actual interrupt of the write by repeating
     * the write until we have no EINTR 'error'
     */
    /* shoudl it be send or write? */
    while( (sent = write(socket, buf_ptr, msg_len)) == -1
	   && errno == EINTR );
    /* Here we handle the possibility of writing less
     * than the entire buffer due to an interrupt.
     */
    if( sent > 0 ) {
      msg_len -= sent;
      buf_ptr += sent;
    }
    else if (sent < 0)  {
      *debug << "ERROR:  writeit()" << endl
	     << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
      conlog << "ERROR:  Socket write error." << endl;
    }
  }
  return sent;
} // end of int conGuy::writeit()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::updateLastSendTime()
//
// Description:
//   Self explanatory I believe...
//
bool conGuy::updateLastSendTime() {
  // I think i need this datastructure as well...
  struct timezone tz;
  
  if(gettimeofday(&lastSendTime, &tz) != 0) {
    *debug << "ERROR:  gettimeofday() failed!" << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    conlog << "ERROR:  Couldn't retrieve current time (flood control)"
	 << endl;
    return false;
  }
  lastSendSize = sendBuff.size();
  return true;
} // end of bool conGuy::updateLastSendTime()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::sleepRequiredTime()
//
// Description:
//   Call this before every send.  (sendBuffOut)
//   It does a sleep() or usleep() as required.
//
bool conGuy::sleepRequiredTime() {
  // Flag to check if I need to sleep.
  bool needToSleep = false;

  // Temp variables for storing the current time.
  struct timeval tv;
  struct timezone tz;

  // Time required to wait.
  long secReq  = sleep_sec_req;
  long usecReq = sleep_usec_req;

  // Time I will sleep:
  long secSleep;
  long usecSleep;

  // Elapsed time.
  long secElapsed;
  long usecElapsed;

  if(gettimeofday(&tv, &tz) != 0) {
    *debug << "ERROR:  gettimeofday() failed!" << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  
  // If the packet size was >= 30 bytes, we use 35 milliseconds per byte.
  if(lastSendSize >= 30) {
    secReq  = (lastSendSize * sleep_usec_per_byte) / 1000000;
    usecReq = (lastSendSize * sleep_usec_per_byte) % 1000000;
  }
  
  // Set elapsed time.
  secElapsed  = tv.tv_sec - lastSendTime.tv_sec;
  usecElapsed = tv.tv_usec - lastSendTime.tv_usec;
  // We can't have negative times, can we?  Fix that.
  if(usecElapsed < 0) {
    secElapsed--;
    usecElapsed += 1000000;
  }

  if(secElapsed < secReq) {
    needToSleep = true;
  }
  if(secElapsed == secReq && usecElapsed < usecReq) {
    needToSleep = true;
  }

  if(needToSleep == true) {
    secSleep  = secReq  - secElapsed;
    usecSleep = usecReq - usecElapsed;
    // We can't have negative times, can we?  Fix that.
    if(usecSleep < 0) {
      secSleep--;
      usecSleep += 1000000;
    }
    if(secSleep  >= 1)
      sleep(secSleep);
    if(usecSleep >= 1)
      usleep(usecSleep);
  }
  return true;
}  // end of bool conGuy::sleepRequiredTime()



//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool conGuy::connectToBnet()
//
// Description:
//   This function processes and does the essential sends & recvs necessary
//   to log on to bnet.
//
//   This is the logon sequence I use:
// 
// SEND -> 	Protocol byte	(01)
// SEND ->	SID_AUTH_INFO	(0x50)
// RECV <-	SID_PING	(0x25)
// RECV <-	SID_AUTH_INFO	(0x50)
// SEND ->	SID_PING	(0x25) [Optional], but I do it.
// SEND ->	SID_AUTH_CHECK	(0x51)
// RECV <-	SID_AUTH_CHECK	(0x51)
// SEND ->	SID_LOGONRESPONSE	(0x29)
// RECV <-	SID_LOGONRESPONSE	(0x29)
// SEND ->	SID_UDPPINGRESPONSE	(0x14) [Game Specific: SEXP/STAR/W2BN]
// SEND ->	SID_ENTERCHAT	(0x0A)
// SEND ->      SID_JOINCHANNEL (0x0C)
// RECV <-      SID_ENTERCHAT	(0x0A)
//
bool conGuy::connectToBnet() {

  int rc;
  struct sockaddr_in localAddr, servAddr;
  struct hostent *h;
  unsigned char conGuyBuff[MAX_MSG];
  // Procedural connect.
  h = gethostbyname(server.c_str());
  if(h == NULL) {
    *debug << "ERROR: Unknown host: " << server << endl;
    conlog << "ERROR: Unknown host: " << server << "  (check connect.cfg)"
	   << endl;
    return false;
  }

  servAddr.sin_family = h->h_addrtype;
  memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
  servAddr.sin_port = htons(short(port));

  /* create socket */
  sd = socket(AF_INET, SOCK_STREAM, 0);
  if(sd<0) {
    *debug << "ERROR:  Can't get a socket bound." << endl;
    conlog << "ERROR:  Couldn't create a socket in connect." << endl;
    return false;
  }


  // Bind any port number
  localAddr.sin_family = AF_INET;
  localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  localAddr.sin_port = htons(0);

  // Client doesn't need to bind to a particular port.
  //   rc = bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr));
  //   if(rc<0) {
  //     *debug << "ERROR:  Can't bind port TCP " << short(port) << "." <<endl;
  //     return false;
  //  }


  *debug << "DEBUG:  Ok, trying to connect..." << endl;
  // Connect to the server.
  rc = connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr));
  if(rc<0) {
    *debug << "ERROR:  Attempt to connect to server failed." << endl;
    conlog << "ERROR:  Attempt to connect to server failed." << endl;
    return false;
  }
  
  // Connected!  YAY!
  *debug << "DEBUG:  We are now connected to server: " << server
	 << " on port " << short(port) << endl;
  conlog << "Successful connection established to server." << endl;
  

  // Set the sd to non-blocking.
  int flags = fcntl(sd, F_GETFL, 0);
  if(fcntl(sd, F_SETFL, flags | O_NONBLOCK) == -1) {
    *debug << "ERROR:  Can't set the socket for non-blocking" << endl;
    conlog << "ERROR:  Setting the connection socket flags failed." << endl;
    close(sd);
    sd = -1;
    return false;
  }

  usleep(110000);

  // Starting to send stuff for the log on process!!
  // See the top of this function for comments on the procedure I use.
  
  bool login_complete = false;

  conlog << "Proceeding to log into account..." << endl;

  // Send PROTOCOL BYTE.  (game protocol)
  if(!s_pkt_protocol()) {
    *debug << "ERROR:    Send protocol byte failed.  " << endl
	   << "          " << __FILE__ << "(" << __LINE__ << ")" << endl;
    conlog << "ERROR:  Send game protocol byte failed." << endl;
    return false;
  }
  *debug << "DEBUG:  Just sent the protocol byte." << endl;

  
  // Send SID_AUTH_INFO.
  if(!s_pkt_SID_AUTH_INFO()) {
    *debug << "ERROR:    Send packet SID_AUTH_INFO failed." << endl
	   << "          " << __FILE__ << "(" << __LINE__ << ")" << endl;
    conlog << "ERROR:    Send packet SID_AUTH_INFO failed." << endl;
    return false;
  }
  *debug << "DEBUG:  Just sent the SID_AUTH_INFO 0x50 packet" << endl;



  
  // Now we wait for responses to the stuff we've sent and react accordingly.
  recvBuff.clear();
  while(!login_complete) {
    // Check to see if we're sure there's not another packet within this
    //   buffer.  I do this because the test program I wrote to serve as a
    //   fake bnet server sent 2 packets in one send.  Weirdness on who's part?
    //   Anyhow, no pointing fingers, I handle this anyways.
    //
    // Update:  I think this is why there is that required send delay of at
    //   least 200 milliseconds.  Thought about it when I did flood control.
    // 
    if(recvBuff.size() == 0) {
      rc = 0;
      while(rc == 0) {
	// Poll variables:
	struct pollfd ufds;
	int timeout = 5000;   // 5000 milliseconds == 5 seconds.  

	ufds.fd     = sd;
	ufds.events = POLLIN;

	if(poll(&ufds, 1, timeout) == 0) {
	  // Poll timeout.  
	  *debug   << "ERROR:  Connection timed out." << endl;
	  conlog << "ERROR:  Connection timed out." << endl;
	  return false;
	}
	else {
	  // This 'else' consists of cases where the poll had something happen.
	  switch (ufds.revents) {
	  case POLLIN:
	  case POLLPRI:
	    // We have stuff to read in.  break & continue on...
	    break;

	  case POLLNVAL:
	    *debug << "ERROR:  Poll err:  fd not open." << endl
		   << "     " << __FILE__ << "(" << __LINE__ << ")" << endl;
	    conlog << "Connection was closed somewhere." << endl;
	    return false;
	    break;
	  case POLLERR:
	    *debug << "ERROR:  Poll err:  errno == " << errno << endl;
	    conlog << "ERROR:  Poll error occurred.  errno == " 
		   << errno << endl;
	    return false;
	    break;
	  case POLLHUP:
	    *debug << "ERROR:  Poll err:  Hung up."  << endl;
	    return false;
	    break;
	  case POLLHUP | POLLERR | POLLIN:
	    *debug << "ERROR:  connectToBnet():  Poll err:  "
		   << "POLLHUP | POLLERR | POLLIN." << endl;
	    conlog << "ERROR:  " << __FILE__ << "(" << __LINE__ << ")"
		   << "Poll err.  POLLHUP | POLLERR | POLLIN." << endl;
	    return false;
	  default:
	    // Weird shit came down.
	    *debug << "ERROR:  connectToBnet():  Unknown poll revents.  "
		   << "revents == ";
	    conlog << "ERROR:  " << __FILE__ << "(" << __LINE__ << ")"
		   << "  Unknown poll revents.  revents == ";

	    for(unsigned int i = 0; i < sizeof(short); i++) {
	      *debug  << print_hex_form( *(((u8*)&ufds.revents) +i) );
	      conlog << print_hex_form( *(((u8*)&ufds.revents) +i) );
	    }
	    *debug << endl;
	    conlog << endl;

	    return false;
	    break;
	  }  // end of switch

	}  // end of "else" for "if(poll(&ufds, 1, timeout) == 0)"


	// Great!  Let's start reading in data now.

	rc = readit(sd, conGuyBuff, MAX_MSG);
	if(rc < 0) {
	  *debug << "ERROR:  Can't readit data.  " << endl
		 << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	  return false;
	}
	*debug << "DEBUG:  got a packet from the server. rc == " << rc << endl;
      }

      recvBuff.clear();
      recvBuff.add(conGuyBuff, rc);
      *debug << "DEBUG:  right before striping" << endl;
    }
    // Strip the header.
    recvBuff.strip();
    *debug << "DEBUG:  got to right before the switch." << endl;



    switch(recvBuff.getPacketID()) {
      

    case SID_PING: {
      u32 pingVal;
      if(! r_pkt_SID_PING(pingVal)) {
	*debug << "ERROR:  r_pkt_SID_PING() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  r_pkt_SID_PING()." << endl;
	return false;
      }
      *debug << "DEBUG:  recv a ping" << endl;
      if(! s_pkt_SID_PING(pingVal)) {
	*debug << "ERROR:  s_pkt_SID_PING() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  s_pkt_SID_PING()." << endl;
	return false;
      }
      *debug << "DEBUG:  sent a ping" << endl;
    } break;



    case SID_AUTH_INFO: {
      if(! r_pkt_SID_AUTH_INFO()) {
	*debug << "ERROR:  r_pkt_SID_AUTH_INFO() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	return false;
      }
      *debug << "DEBUG:  recv SID_AUTH_INFO..." << endl;
      
      if(! s_pkt_SID_AUTH_CHECK()) {
	*debug << "ERROR:  s_pkt_SID_AUTH_CHECK() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  s_pkt_SID_AUTH_CHECK()." << endl;
	return false;
      }
      *debug << "DEBUG:  sent SID_AUTH_CHECK..." << endl;
    } break;
    


    case SID_AUTH_CHECK: {
      if(! r_pkt_SID_AUTH_CHECK()) {
	*debug << "ERROR:  r_pkt_SID_AUTH_CHECK() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	return false;
      }
      if(! s_pkt_SID_LOGONRESPONSE()) {
	*debug << "ERROR:  s_pkt_SID_LOGONRESPONSE() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  s_pkt_SID_LOGONRESPONSE()." << endl;
	return false;
      }
    } break;



    case SID_LOGONRESPONSE: {
      if(! r_pkt_SID_LOGONRESPONSE()) {
	*debug << "ERROR:  r_pkt_SID_LOGONRESPONSE() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  r_pkt_SID_LOGONRESPONSE()." << endl;
	return false;
      }
      *debug << "DEBUG:  just received SID_LOGONRESPONSE" << endl;

      if(! s_pkt_SID_UDPPINGRESPONSE()) {
	*debug << "ERROR:  s_pkt_SID_UDPPINGRESPONSE() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  s_pkt_SID_UDPPINGRESPONSE()." << endl;
	return false;
      }
      *debug << "DEBUG:  just sent SID_UDPPINGRESPONSE" << endl;
      
      if(! s_pkt_SID_ENTERCHAT()) {
	*debug << "ERROR:  s_pkt_SID_ENTERCHAT() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  s_pkt_SID_ENTERCHAT()." << endl;
	return false;
      }
      *debug << "DEBUG:  just sent SID_ENTERCHAT" << endl;

      if(! s_pkt_SID_JOINCHANNEL(home_channel)) {
	*debug << "ERROR:  s_pkt_SID_JOINCHANNEL(home_channel) broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  s_pkt_SID_JOINCHANNEL(home_channel)." << endl;
	return false;
      }
      *debug << "DEBUG:  just sent SID_JOINCHANNEL" << endl;
    } break;
    


    case SID_ENTERCHAT: {
      if(! r_pkt_SID_ENTERCHAT()) {
	*debug << "ERROR:  r_pkt_SID_ENTERCHAT() broke." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  r_pkt_SID_ENTERCHAT()." << endl;
	return false;
      }
      *debug << "DEBUG:  just got SID_ENTERCHAT" << endl;
      login_complete = true;
    } break;


    case SID_NULL: {
      // do nothing.
      *debug << "DEBUG:  got a SID_NULL from the server (nothing bad)." << endl
	     << "          " << __FILE__ << "(" << __LINE__ << ")" << endl;
    } break;
    
    case 0x4A: {
      // Just trash the packet and do nothing.  This is just a 
      //   request for user stats and comp info.
      //   I don't (I think?) need to send anything back and can ignore it.
      *debug << "DEBUG:  got a 0x4A from the server (nothing bad)." << endl
	     << "          " << __FILE__ << "(" << __LINE__ << ")" << endl;
      if(! trash_curr_packet()) {
	*debug << "ERROR:  Problem trashing packet 0x4A." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	conlog << "ERROR:  Problem trashing packet 0x4A." << endl;
	return false;
      }
    } break;

    default:
      // shit! what do we do?  stuff's comin all weird, wrong or out of order.
      *debug << "ERROR:  Unexpected packet: " 
	     << print_hex_form(recvBuff.getPacketID()) << endl
	     << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
      *debug << "Dumping recvBuff..." << endl;
      recvBuff.print_buffer();
      conlog << "ERROR:  We got an unexpected packet." << endl;
      return false;
    }  // end of switch(recvBuff.getPacketID())
  } // end of while(!login_complete)

  // OMG, it completed!?
  return true;
}  // end of bool conGuy::connectToBnet()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::sendBuffOut()
//
// Description:
//   This function not only sends the sendBuff to bnet, but it also 
//   manages the delay required to avoid flooding off bnet and getting 
//   IP banned.
//
bool conGuy::sendBuffOut() {
  void* ourData = sendBuff.getPtr();
  int rc;

  // Flood protection: 
  // Do I need to sleep required time on a ping return?  The normal answer
  //   would be that I shouldn't need to.  (It would make the ping time
  //   perceived by bnet to be incorrect.  (6r)).  Here I am fearful
  //   that because the server sends us a lot of pings, that our return
  //   pings may cause a weird 'flood' error on their end.  And since it's
  //   not chat sends/coommands that it floods on, it may exit differently.
  //   I suppose that for now, just to be safe, I will sleep the required
  //   time.  As for how I will handle the SID_PING returns, check the
  //   code for that one.  If you don't know what(where) I'm talking about,
  //   it's in the gimmeEvent() function, and inside that function, the
  //   call to handle_nonchat().  

  //  if( (sendBuff.getPacketID() != SID_PING) && (!sleepRequiredTime()) ) {
  if( !sleepRequiredTime()) {
    *debug << "ERROR: sleepRequiredTime() failed on a sendBuffOut()" << endl;
    conlog << "ERROR:  Problems encountered with flood control." << endl;
    return false;
  }
  rc = writeit(sd, ourData, sendBuff.size());
  if(rc < 0) {
    *debug << "ERROR:  Can't send data.  (sendBuffOut())" << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!updateLastSendTime()) {
    // Should this be an error or a warning?
    *debug << "ERROR:  updateLastSendTime() failed in sendBuffOut()" << endl;
    conlog << "ERROR:  Problems encountered with flood control." << endl;
    // Should I return false or true?  Technically it DID send... 
    // Eh, chances are that the update time function fails is too low to care.
    return false;
  }
  return true;
}  // end of bool conGuy::sendBuffOut()



//////////////////////////////////////////////////////////////////////////////
// 
// Function:  bool conGuy::s_pkt_protocol()
//
// Description:
//   Send protocol byte.  (game protocol)
//
bool conGuy::s_pkt_protocol() {
  unsigned char protocol_byte = 0x01;  // game protocol == 0x01.
  int rc;
  rc = writeit(sd, &protocol_byte, 1 /* byte */);
  if(rc < 0) {
    *debug << "ERROR:  Can't send data for protocol byte..  " << endl;      
    return false;
  }
  usleep(70000);  // Protection against more than 1 send merging with another.
  // It's also to avoid flooding and getting IP banned.  
  return true;
}  // end of bool conGuy::s_pkt_protocol()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_AUTH_INFO()
//
// Description:
//   Send SID_AUTH_INFO packet.  This is just basic stuff about the computer
//   running the program.
//
bool conGuy::s_pkt_SID_AUTH_INFO() {
  u8 packetID = (u8)SID_AUTH_INFO;
  u32 protocolID = 0;
  char platformID[5]  = "68XI";
  char programID[5]   = "PXES";
  u32 versionByte     = (u32)0xcd; // (u32)0xc9 for version 1.11, 1.11b, 
                                   // (u32)0xcb for 1.12
                                   // (u32)0xcd for 1.13, 1.13b, 1.13c, 
                                   //               1.13d, 1.13e
  u32 productLanguage = (u32)0x00;    // unknown.  always 0.
  u32 localIP         = (u32)0x00;    // It should log on ok...
  u32 timeBias        = (u32)0x012c;  // EST.  East side baby.
  u32 localeID        = (u32)0x0409;  // english
  u32 languageID      = (u32)0x0409;  // english
  char countryAbbrev[4]  = "USA";
  char country[14]       = "United States";

  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  sendBuff.add(&protocolID,      sizeof(u32));
  sendBuff.add(platformID, 4);
  sendBuff.add(programID,  4);
  sendBuff.add(&versionByte,     sizeof(u32));
  sendBuff.add(&productLanguage, sizeof(u32));
  sendBuff.add(&localIP,         sizeof(u32));
  sendBuff.add(&timeBias,        sizeof(u32));
  sendBuff.add(&localeID,        sizeof(u32));
  sendBuff.add(&languageID,      sizeof(u32));
  sendBuff.add(countryAbbrev,    strlen(countryAbbrev)+1);
  sendBuff.add(country,          strlen(country)+1);
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_AUTH_INFO failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_AUTH_INFO failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();
  return true;
}  // end of bool conGuy::s_pkt_SID_AUTH_INFO()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_PING(u32 &pingValue)
//
// Description:
//   Receive a ping value.
//
bool conGuy::r_pkt_SID_PING(u32 &pingValue) {
  // Return the successfulness of extraction.
  return recvBuff.extract(pingValue);
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_PING(u32 pingValue)
//
// Description: 
//   This function sends back the ping we got from r_pkt_SID_PING().
//   
// Note:
//   I don't think I send back the ping anymore.  I had some glitchy problems
//   with this.  If you have time, look this problem over again.  (ping return
//   is not essential).
// 
bool conGuy::s_pkt_SID_PING(u32 pingValue) {
  // Send it back.
  sendBuff.clear();
  sendBuff.setPacketID(SID_PING);
  if(! sendBuff.add(&pingValue, sizeof(u32))) {
    *debug << "ERROR:  in s_pkt_SID_PING(), couldn't add pingValue to sendBuff"
	   << endl << "             " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    return false;
  }
  if(! sendBuff.package()) {
    *debug << "ERROR:  in returnPing(), couldn't package sendBuff"
	   << endl << "             " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    return false;
  }
  return sendBuffOut();  
}  // end of bool conGuy::s_pkt_SID_PING(u32 pingValue)



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_AUTH_INFO()
//
// Description:
//   This packet gives us variables and the equation used to do the 
//   game version check.
//
bool conGuy::r_pkt_SID_AUTH_INFO() {
  // This is the format of the packet.
  
  //     (DWORD)		 Logon Type
  //     (DWORD)		 Server Token
  //     (DWORD)		 UDPValue**
  //     (FILETIME)	 MPQ filetime
  //     (STRING)		 IX86ver filename
  //     (STRING)		 ValueString


  u32 logonType;     // Logon Type.  (0 == Broken Sha-1 (STAR/SEXP/D2DV/D2XP)
                     //               1 == NLS version 1 (War3Beta),
                     //               2 == NLS Version 2 (WAR3))
  u32 serverToken;

  u32 udpValue;       // We won't use this.  See note below.
  timeval mpqFiletime;// We wont' use this.

  char hash_mpq[64];   // We extract the mpq ver from this.
  char hash_equ[128];   // This contains the equation.
  
  // Note about udpValue:
  //   No one really knows what this is, however, it is used in 2nd DWORD of 
  //   the UDP packet PKT_CONNTEST2 (0x09). It has also been seen in the realm 
  //   logon blob.  PKT_CONNTEST2 is used to establish that the client supports
  //   UDP.

  // Extract the packet stuff's.
  recvBuff.extract(logonType);
  recvBuff.extract(serverToken);
  recvBuff.extract(udpValue);
  recvBuff.extract(mpqFiletime);
  recvBuff.extract(hash_mpq);
  recvBuff.extract(hash_equ);
  
  // Set the values in the authGuy object.
  auth.setLogonType(logonType);
  auth.setServerToken(serverToken);
  auth.setUdpValue(udpValue);
  auth.setMpqFiletime(mpqFiletime);
  auth.setHash_mpq(hash_mpq);
  auth.setHash_equ(hash_equ);
  

  return true;
}  // end of bool conGuy::r_pkt_SID_AUTH_INFO()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_AUTH_CHECK()
//
// Description:
//   This packet sends out the info concerning the game version checking.
//
bool conGuy::s_pkt_SID_AUTH_CHECK() {
  u32 cdkeyValue2;
  u8 packetID = SID_AUTH_CHECK;

  // Below are the elements of this packet in this order.
  u32 clientToken;
  u32 exeVer;
  u32 checkRevisionResult;
  u32 numKeys;
  u32 spawn;
  u32 keyLength;
  u32 cdkeyValue0;  // (product)
  u32 cdkeyValue1;
  u32 zero_u32 = 0x00;  // unknown field.
  u32 *hashed_data;  // This points to 5 u32's.  (20 bytes).
  string exe_info;
  string owner_name;

  // Store stor'in thingS: 
  clientToken = auth.getClientToken();
  // TODO:  Learn how to get the EXE Version (u32) in here sometime.
  //     (hardcoded for now.  FIX!)
  //   exeVer = 0x01010101;   // for v 1.11
  //   exeVer = 0x01010103;   //  for v 1.11b
  //   exeVer = 0x00000001;   // for v 1.12  (but then it stopped working...)
  //   exeVer = 0x01010203;   // for v 1.12
  //   exeVer = 0x01010301;   // for v 1.13
  //   exeVer = 0x01010303;   // for v 1.13b
  //   exeVer = 0x01010305;   // for v 1.13c
  //   exeVer = 0x01010307;   // for v 1.13d
  //   exeVer = 0x01010309;   // for v 1.13e
  exeVer = 0x0101030b;   // for v 1.13f
  checkRevisionResult = auth.checkRevision();
  numKeys = 0x01;  // only 1 key for sc/bw.
  spawn   = 0x00;  // no spawn
  keyLength = 0x0d;  // sc/bw has key length of 13
  
  if(!auth.getCDVars()) {
    *debug << "ERROR:   getCDVars failed." << endl
	   << "         " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  cdkeyValue0 = auth.getCdkeyVar0();
  cdkeyValue1 = auth.getCdkeyVar1();
  cdkeyValue2 = auth.getCdkeyVar2();
  
  
  // DEBUG output:
  *debug << "DEBUG:  CDkey values:" << endl;
  for(unsigned int i = 0; i < sizeof(u32); i++) {
    *debug << print_hex_form( *(((u8*)&cdkeyValue0) +i) );
  }
  *debug << endl;  
  for(unsigned int i = 0; i < sizeof(u32); i++) {
    *debug << print_hex_form( *(((u8*)&cdkeyValue1) +i) );
  }
  *debug << endl;  
    for(unsigned int i = 0; i < sizeof(u32); i++) {
    *debug << print_hex_form( *(((u8*)&cdkeyValue2) +i) );
  }
  *debug << endl;  

  hashed_data = auth.getHashData();

  exe_info = auth.getExeInfo();
  owner_name = "GiTMbot user";

  // done setting the vars we need, now prepare the packet.
  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  sendBuff.add(&clientToken,         sizeof(u32));
  sendBuff.add(&exeVer,              sizeof(u32));
  sendBuff.add(&checkRevisionResult, sizeof(u32));
  sendBuff.add(&numKeys,             sizeof(u32));
  sendBuff.add(&spawn,               sizeof(u32));
  sendBuff.add(&keyLength,           sizeof(u32));
  sendBuff.add(&cdkeyValue0,         sizeof(u32));
  sendBuff.add(&cdkeyValue1,         sizeof(u32));
  sendBuff.add(&zero_u32,            sizeof(u32));
  for(int i = 0; i < 5; i++)
    sendBuff.add(&hashed_data[i],    sizeof(u32));
  sendBuff.add(exe_info.c_str(),     exe_info.length()+1);
  sendBuff.add(owner_name.c_str(),   owner_name.length()+1);


  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_AUTH_CHECK failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_AUTH_CHECK failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }

//   *debug << "DEBUG:  Here is the sent SID_AUTH_CHECK packet:" << endl;
//   sendBuff.print_buffer();
  sendBuff.clear();
  

  return true;

} // end of bool conGuy::s_pkt_SID_AUTH_CHECK()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_AUTH_CHECK()
//
// Description:
//   This packet gives us the result of our game version check send.
//
bool conGuy::r_pkt_SID_AUTH_CHECK() {
  u32 result;  // result of the auth
  string additionalInfo;  // Additional info msg.
  
  recvBuff.extract(result);
  recvBuff.extract(additionalInfo);

  switch (result) {
  case 0x000:
    // Auth challenge passed.
    return true;
    break;
  case 0x100:
    *debug << "ERROR:  Old Game Version.  Patch MPQ file: "
	   << additionalInfo << endl;
    conlog << "ERROR:  Old Game Version.  Patch MPQ file: "
	   << additionalInfo << endl;
    return false;
    break;
  case 0x101:
    *debug << "ERROR:  Invalid Game Version.  " << endl;
    conlog << "ERROR:  Invalid Game Version.  " << endl;
    return false;
  case 0x200:
    *debug << "ERROR:  Invalid CD key. " << endl;
    conlog << "ERROR:  Invalid CD key. " << endl;
    return false;
    break;
  case 0x201:
    *debug << "ERROR:  CD key is in use!  OMFG.  Kill this guy: " 
	   << additionalInfo << endl;
    conlog << "ERROR:  CD key is in use!  OMFG.  Kill this guy: " 
	   << additionalInfo << endl;
    return false;
    break;
  case 0x202:
    *debug << "ERROR:  Aww shitters.  You're using a banned cd key." << endl;
    conlog << "ERROR:  Aww shitters.  You're using a banned cd key." << endl;
    return false;
    break;
  case 0x203:
    *debug << "ERROR:  Wrong Product." << endl;
    conlog << "ERROR:  Wrong Product." << endl;
    return false;
    break;
  default:
    *debug << "ERROR:  Unknown response for SID_AUTH_CHECK (0x51)." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    conlog << "ERROR:  Unknown response for SID_AUTH_CHECK (0x51)." << endl;
    return false;
    break;
  }
  return false;
}  // end of bool conGuy::r_pkt_SID_AUTH_CHECK()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_LOGONRESPONSE()
//
// Description: 
//   Send out the logon info.  This includes the hash of the password and
//   client and server tokens.  The account name is also sent as a string.
//
bool conGuy::s_pkt_SID_LOGONRESPONSE() {
  u32 packetID = SID_LOGONRESPONSE;
  u32 clientToken = auth.getClientToken();
  u32 serverToken = auth.getServerToken();
  u32 password_hash[5];
  string account = auth.getAccount();


  // copy the password_hash over from the authGuy object.
  memcpy(password_hash, auth.getPasswordHash(), sizeof(u32) * 5);

  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  sendBuff.add(&clientToken,         sizeof(u32));
  sendBuff.add(&serverToken,         sizeof(u32));
  sendBuff.add(password_hash,        sizeof(u32) * 5);
  sendBuff.add(account.c_str(),      account.length() + 1);
  
  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_LOGONRESPONSE failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_LOGONRESPONSE failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();
  return true;
} // end of bool conGuy::s_pkt_SID_LOGONRESPONSE()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_LOGONRESPONSE() 
//
// Description:
//   This packet gives us the status response to our password hash send.
//
bool conGuy::r_pkt_SID_LOGONRESPONSE() {
  // result of the password send.
  u32 result;
  recvBuff.extract(result);
  
  if(result == 0x01) {
    // login success.
    *debug << "DEBUG:  Logon success" << endl;
    conlog << "Account and password logon success." << endl;
    return true;
  }
  else if(result == 0x00) {
    // invalid password.
    *debug << "ERROR:  Invalid password" << endl;
    conlog << "ERROR:  Invalid password" << endl;
    return false;
  }
  else {
    // uhh.... dunno what this. 
    *debug << "ERROR:  Unknown SID_LOGONRESPONSE from server.  Got result == ";
    conlog << "ERROR:  Unknown SID_LOGONRESPONSE from server.  Got result == ";
    for(unsigned int i = 0; i < sizeof(u32); i++) {
      *debug << print_hex_form( *(((u8*)&result) +i) );
      conlog << print_hex_form( *(((u8*)&result) +i) );
    }
    return false;
  }
} // end of bool conGuy::r_pkt_SID_LOGONRESPONSE()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_UDPPINGRESPONSE()
//
// Description:
//   This packet lets bnet know we can receive UDP packets.  We won't handle
//   UDP packets though.  I'm sending this because it's part of the logon
//   sequence and we can use it to set our ping to essentially 0.
// 
// Note: 
//   I'm not sure if that statement above is accurate.  I have shitty memory
//   and can't remember what I actually chose to do when I coded it.
//   Probably another good reason to comment as I code things...
//
bool conGuy::s_pkt_SID_UDPPINGRESPONSE() {
  u8 packetID = SID_UDPPINGRESPONSE;
  u32 udpCode;  // This is the value given to us via UDP packet 
                // PKT_SERVERPING (0x05).

                // For 0 ping, send "tenb".

  // Memcpy the DWORD(u32) of the string "bnet", which is "tenb".
  memcpy(&udpCode, "tenb", strlen("tenb"));    // (without the null)

  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  sendBuff.add(&udpCode,      sizeof(u32));
  
  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_UDPPINGREPONSE failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_UDPPINGRESPONSE failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();  
  return true;
}  // end of bool conGuy::s_pkt_SID_UDPPINGRESPONSE()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_ENTERCHAT()
//
// Description:
//   This function requests a unique username for the account so we can 
//   "enter" chat.
//
bool conGuy::s_pkt_SID_ENTERCHAT() {
  u8 packetID = SID_ENTERCHAT;
  string accountName = auth.getAccount();
  string statString = "";  // Just NULL for cdkey-ed products.
  
  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  sendBuff.add(accountName.c_str(),      accountName.length()+1);
  sendBuff.add(statString.c_str(),       statString.length() +1);
  
  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_ENTERCHAT failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_ENTERCHAT failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();  
  return true;
} // end of bool conGuy::s_pkt_SID_ENTERCHAT()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_ENTERCHAT() 
//
// Description:
//   Receive the enterchat packet.  We get a unique username here.  
//   (which could be just the account name or the account name 
//   with #2, #3 etc.  appended to the end.
//
bool conGuy::r_pkt_SID_ENTERCHAT() {
//   *debug << "DEBUG:  data in r_pkt_SID_ENTERCHAT: " << endl << endl;
//   recvBuff.print_buffer();
//   *debug << endl;

  string statstring;  // We'll not save this anywhere.
  
  recvBuff.extract(unique_username);
  recvBuff.extract(statstring);    // This is the 'record' games of the player
  recvBuff.extract(account);
  
  return true;
  
} // end of bool conGuy::r_pkt_SID_ENTERCHAT()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_GETCHANNELLIST()
//
// Description:
//   Request for the available public channels
//
// Notes:
//   Getting the channel list has no purpose for the bot.  At one point
//   I thought it was necessary in the login procedure.  Apparently not! :)
// 
bool conGuy::s_pkt_SID_GETCHANNELLIST() {
  u8 packetID   = SID_GETCHANNELLIST;
  u32 productID = BROOD_WAR_PRODUCT_ID;    // #define in conGuy.h
  
  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  sendBuff.add(&productID, sizeof(u32));
  

//   *debug << "DEBUG:  This is what our pkt send SID_GETCHANNELLIST looks like:"
// 	 << endl << endl;
//   sendBuff.print_buffer();  // debug
//   *debug << endl;


  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_GETCHANNELLIST failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_GETCHANNELLIST failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();    
  
  
  return true;
}  // end of bool conGuy::s_pkt_SID_GETCHANNELLIST()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_GETCHANNELLIST()
//
// Description: 
//   Receive a whole bunch of channel names that are available.
//
// Other notes:
//   This packet has a list of null-terminated strings
//   (actually they are UTF-8, but I parse them as ascii)
//   which are terminated by a empty string.
//   I'm just throwing them away.  
//
bool conGuy::r_pkt_SID_GETCHANNELLIST() {
//   *debug << "DEBUG:  This is what our r_pkt_SID_GETCHANNELLIST looks like:"
// 	 << endl << endl;
//   recvBuff.print_buffer();
//   *debug << endl;
  
  string aChannel;
  recvBuff.extract(aChannel);
  while(aChannel.length() != 0) {
    recvBuff.extract(aChannel);
  }
  
  return true;
} // end of bool conGuy::r_pkt_SID_GETCHANNELLIST()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_CHATCOMMAND(string msg)
//
// Description:  
//   Send some text just like you would in the actual brood war game.
//   This includes /join 's and such.
//
bool conGuy::s_pkt_SID_CHATCOMMAND(string msg) {
  u8 packetID = SID_CHATCOMMAND;
  string command = msg;

  sendBuff.clear();
  sendBuff.setPacketID(packetID);
  

  sendBuff.add(command.c_str(), command.length()+1);

  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_CHATCOMMAND failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_CHATCOMMAND failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();    

  return true;
} // end of bool conGuy::s_pkt_SID_CHATCOMMAND(string msg)


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_JOINCHANNEL(string channel)
//
// Description:
//   I use this only in the beginning to join a channel opon login.
//   I use this because I suppose it's a request to join a channel before
//   we have officially "entered chat".  After we've offically "entered chat"
//   I join channels by just plain sending the "/join <channel>" as a 
//   chat command/message.
//
// Other notes:
//
//   Join types (flags value):						
//     0x00 - Standard request to join; bnet will notify you if the channel
//       is full or restricted.						
//     0x01 - Join product-specific channel. W3 clients on accounts in a 
//       clan will end up in their Clan channel.       
//     0x02 - Create channel. Bnet will ipban if the channel is restricted,  
//       but this can be used to join full channels.			 
//
//   From my packet logs, it seems as if 0x02 joins existing channels as well.
//   Dunno... I'll just always send 0x02 as my flag.  We can fix this
//   later as it works as just the initial log in send.
//
bool conGuy::s_pkt_SID_JOINCHANNEL(string channel) {

  u8 packetID = SID_JOINCHANNEL;
  u32 flags = 0x02;
  string toJoin = channel;


  sendBuff.clear();
  sendBuff.setPacketID(packetID);

  sendBuff.add(&flags, sizeof(u32));
  sendBuff.add(toJoin.c_str(), toJoin.length()+1);
  
  // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_JOINCHANNEL failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_JOINCHANNEL failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();    
  return true;
}  // end of bool conGuy::s_pkt_SID_JOINCHANNEL(string channel)


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_NULL()
//
// Description:
//   This is a blank packet just to help keep the connection alive.
//
bool conGuy::s_pkt_SID_NULL() {
  u8 packetID = SID_NULL;
  
  sendBuff.clear();
  sendBuff.setPacketID(packetID);
    // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_NULL failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_NULL failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();    
  return true;
}  // end of bool conGuy::s_pkt_SID_NULL()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::s_pkt_SID_LEAVECHAT()
//
// Description:
//   This is sent right before disconnecting.  Leaves 'chat'.
//
// Notes:
//   I have currently taken this packet out of the logout sequence.  (See the
//   disconnectNow() function.)  The reason is because sometimes
//   I would get some kind of error of some sort, or somehow get into some
//   state in which I would need to disconnect.  The disconnect function
//   would call this s_pkt_SID_LEAVECHAT() function and try to send another
//   packet to an already screwed up socket descriptor.  And so... the next 
//   time I start up the bot, the OS gives me some weird error(sorry, I didn't
//   write down what it was.)  about the socket.  Not sending this 
//   packet keeps me from getting that weird error.  We can see what it is
//   again if we take the chat client version of GiTMbot and uncomment
//   the call to this function in disconnectNow().  You'll see it eventually.
//   
//   TODO:  A solution that may work and might be implemented one day is to
//   do a quickly-returning poll on the socket and then check if the 
//   error returned is something that tells us the socket has been closed.
//
bool conGuy::s_pkt_SID_LEAVECHAT() {
  u8 packetID = SID_LEAVECHAT;
  
  sendBuff.clear();
  sendBuff.setPacketID(packetID);
    // Package and send the packet off.
  if(!sendBuff.package()) {
    *debug << "ERROR:  packaging for packet SID_LEAVECHAT failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  if(!sendBuffOut()) {
    *debug << "ERROR:  send for packet SID_LEAVECHAT failed." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  sendBuff.clear();    
  return true;
}  // end of bool conGuy::s_pkt_SID_LEAVECHAT()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_FLOODDETECTED()
//
// Description:
//   This function is here to make it consistent with the way I'm 
//   doing everything already.
//
bool conGuy::r_pkt_SID_FLOODDETECTED() {
  // This packet has no data.
  return true;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::r_pkt_SID_MESSAGEBOX()
//
// Description:
//   This function receives the data that would be received if there was a
//   message box that needed to be displayed.  The data in this packet
//   would normally be able to be passed directly into a MS VC++ or MS VB
//   messagebox() function.  Since I'm using g++/gcc, I'll just log
//   the contents of the data here.
//   
bool conGuy::r_pkt_SID_MESSAGEBOX() {
  u32 style;
  string caption;
  string text;
  
  recvBuff.extract(style);
  recvBuff.extract(caption);
  recvBuff.extract(text);

  *debug   << "DEBUG: msgbox:   " << caption << endl << endl
	   << "                 " << text    << endl << endl;
  conlog << "MessageBox:    " << caption << endl << endl
	   << "               " << text    << endl << endl;
  return true;
}  //  end of    bool conGuy::r_pkt_SID_MESSAGEBOX()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  event conGuy::gimmeEvent()
//
// Description:
//   Returns an event object of the next event that happened in the 
//   current channel that the conGuy is in.
//
// Fixes:
//
// 8/14/2004
// As a fix to this s_pkt_SID_NULL, I'm going to instead just say
// something in the channel.
//
event conGuy::gimmeEvent() {
  unsigned char buff[MAX_MSG];  // stores socket recv-ed stuff.
  event newEvent;   // <-- return value.

  // If the !exit command was used, then just return a useless newEvent.
  // If the conGuy is not connected, just return a useless newEvent.
  if(done == true || isConnected() == false) {
    newEvent.try_again = true;
    return newEvent;
  }

  // If the recvBuff.size() == 0, we need to read from the socket for more
  //   stuff untill we get a real packet.
  if(recvBuff.size() == 0) {
    int rc = 0;
    while(rc == 0) {
      
      // Poll variables:
      struct pollfd ufds;
      int timeout = 100; // milliseconds

      ufds.fd     = sd;
      ufds.events = POLLIN;
      if(poll(&ufds, 1, timeout) == 0) {
	// If it reached here, it timed out after timeout milliseconds.
	//   
	// What we need to do next is check to see if we need to
	//   send a SID_NULL packet to keep the connection active (if
	//   the time that has passed warrants it.
	//  
	// Then check to see if we have anything in our send
	//   queue and send a send if the queue is non-empty.  
	//
	// Finally, return an event object that has the
	//   try_again flag on, telling the calling object to try
	//   to get an event again.
	// 
	

	// Check to see if we need to send a SID_NULL.
	timeval currTime;
	struct timezone tz;
	gettimeofday(&currTime, &tz);

	// TODO: Fix this bug.  It's actually supposed to be the last
	// time it hears something, not the last time it actually says
	// something.
	if(currTime.tv_sec - lastSendTime.tv_sec > SECONDS_SID_NULL) {
	  // Time to send SID_NULL packet!
// 	  *debug << "DEBUG:  It's been quite a long time.  "
// 		 << "Let's do s_pkt_SID_NULL..." << endl;
// 	  if(! s_pkt_SID_NULL()) {
// 	    *debug << "ERROR:  send for packet SID_NULL failed." << endl
// 		   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
// 	    conlog << "ERROR:  s_pkt_SID_NULL() failed." << endl;
// 	    newEvent.exit_now = true;
// 	    return newEvent;
// 	  }
	  // For debug timestamp.
	  time_t rawtime;
	  struct tm * timeinfo;
	  time ( &rawtime );
	  timeinfo = localtime ( &rawtime );

	  *debug << "DEBUG:  It woulda been time to send s_pkt_SID_NULL."
		 << "(saying some msg in channel)" << endl 
		 << "       The time is " << string(asctime(timeinfo)) << endl
		 << "       currTime.tv_sec     == " << currTime.tv_sec << endl
		 << "       lastSendTime.tv_sec == " << lastSendTime.tv_sec
		 << endl << "       SECONDS_SID_NULL    == " 
		 << SECONDS_SID_NULL << endl;

	  // 8/14/2004
	  // As a fix to this s_pkt_SID_NULL, I'm going to instead just say
	  // something in the channel.
          // if(! say(string("It's getting pretty damn quiet in here...") +
          // string("Even bots get lonely."))) {
          // *debug << "ERROR:  send for packet SID_CHATCOMMAND failed."
          // << endl;
          // conlog << "ERROR:  Send for packet SID_CHATCOMMAND failed."
          // << endl;
          // }
	  
	  // Set the lastSendTime to the currTime.  (Reset the timer)
	  lastSendTime = currTime;
	  
	} // end of case where it's time to send SID_NULL packet.
	
	// Aquire a lock on the mutex for our sendQueue, then send
	//   an item in it if it has anything in it.
	int rc;
	rc = pthread_mutex_lock(&toSend.mutex);
	if(toSend.out.size() > 0) {
	  // Goodie.  We have some treats for us!
	  string msg = toSend.out.front();
	  toSend.out.pop_front();
	  
	  if(! say(msg)) {
	    *debug << "ERROR:  send for packet SID_CHATCOMMAND failed."
		   << endl;
	    conlog << "ERROR:  Send for packet SID_CHATCOMMAND failed."
		   << endl;
	    rc = pthread_mutex_unlock(&toSend.mutex);
	    newEvent.exit_now = true;
	    return newEvent;
	  }
	}
	rc = pthread_mutex_unlock(&toSend.mutex);


	// Acquire a lock on our mutex for our sendRequests requests.
	//   Send stuff it has stuff needing to be sent.
	rc = pthread_mutex_lock(&requests.mutex);
	if(requests.out.size() > 0) {
	  // Goodie, we have some conGuys that would like our services.
	  string msg         = requests.out.front();
	  conGuy *comingFrom = requests.fromConGuy.front();
	  string fromUser    = requests.fromWho.front();
	  requests.out.pop_front();
	  requests.fromConGuy.pop_front();
	  requests.fromWho.pop_front();
	  // Send it
	  if(! say(msg)) {
	    *debug << "ERROR:  send for packet SID_CHATCOMMAND failed."
		   << endl;
	    conlog << "ERROR:  Send for packet SID_CHATCOMMAND failed."
		   << endl;
	    rc = pthread_mutex_unlock(&requests.mutex);
	    newEvent.exit_now = true;
	    return newEvent;
	  }
	  else {
	    // Push onto 'awaiting' lists.
	    requests.waiting.push_back(msg);
	    requests.returnToConGuy.push_back(comingFrom);
	    requests.returnToWho.push_back(fromUser);
	    *debug << "just push_back-ed onto waiting list.  "
		   << "waiting.size() == " << requests.waiting.size() << endl;
	  }
	}
	rc = pthread_mutex_unlock(&requests.mutex);


	// Ask the calling object to try to get an event again because
	//   this event has no useful infomation in it.
	newEvent.exit_now = false;
	newEvent.try_again = true;
	return newEvent;
      }
      else {

	// This 'else' consists of the cases where the poll had somethin happen
	switch (ufds.revents) {
	case POLLIN:
	case POLLPRI:
	  // We have stuff to read in.  break & continue on...
	  break;
	case POLLNVAL:
	  *debug << "ERROR:  Poll err:  fd not open. " << endl
		 << "    " << __FILE__ << "(" << __LINE__ << ")" << endl;
	  conlog << "Connection was closed somewhere." << endl;
	  // Don't set the exit_now flag.  We want this thread NOT to exit.
	  //newEvent.exit_now = true;
	  newEvent.try_again = true;
	  // Assert that we really are not connected and that the sd is closed.
	  assert(isConnected() == false);
	  return newEvent;
	  break;
	case POLLERR:
	  *debug << "ERROR:  Poll err:  errno == " << errno << endl;
	  conlog << "ERROR:  Poll error occurred.  errno == " << errno << endl;
	  newEvent.exit_now = true;
	  return newEvent;
	  break;
	case POLLHUP:
	  *debug << "ERROR:  Poll err:  Hung up."  << endl;
	  sleep(60);
	  newEvent.exit_now = false;
	  newEvent.try_again = true;
	  return newEvent;
	  break;
	case POLLHUP | POLLERR | POLLIN: {
	  // For debug timestamp.
	  time_t rawtime;
	  struct tm * timeinfo;
	  time ( &rawtime );
	  timeinfo = localtime ( &rawtime );
	  string currTime = string(asctime(timeinfo));

	  *debug << "ERROR:  Poll err:  POLLHUP | POLLERR | POLLIN." 
		 << currTime << endl;
	  conlog << "ERROR:  Poll hung up.   " << currTime << endl;
	  *debug << "Trying to reconnect..." << endl;
	  conlog << "Trying to reconnect..." << endl;
	  if(!disconnectNow() || sleep(60) || !connectNow(gateway)) {
	    *debug << "Attempt to reconnect gateway " 
		<< getGateway() << " FAILED." << endl;
	    conlog << "Attempt to reconnect FAILED." << endl;
	    newEvent.exit_now = true;
	    newEvent.try_again = false;
	  }
	  else {
	    *debug << "Attempt to reconnect gateway "
		<< getGateway() << " SUCCESS." << endl;
	    conlog << "Attempt to reconnect SUCCESS." << endl;
	    newEvent.exit_now = false;
	    newEvent.try_again = true;
	  }
	  return newEvent;
	} break;
	default:
	  // Weird shit came down.
	  *debug << "ERROR:  gimmeEvent():  Unknown poll revents. revents == ";
	  conlog << "ERROR:  " << __FILE__ << "(" << __LINE__ << ")"
		 << "  Unknown poll revents.  revents == ";
	  for(unsigned int i = 0; i < sizeof(short); i++) {
	    *debug << print_hex_form( *(((u8*)&ufds.revents) +i) );
	    conlog << print_hex_form( *(((u8*)&ufds.revents) +i) );
	  }
	  *debug << endl << "Trying to reconnect...";
	  conlog << endl << "Trying to reconnect...";
	  if(!disconnectNow() || sleep(60) || !connectNow(gateway)) {
	    *debug << "FAILED." << endl;
	    conlog << "FAILED." << endl;
	    newEvent.exit_now = true;
	    newEvent.try_again = false;
	  }
	  else {
	    *debug << "SUCCESS." << endl;
	    conlog << "SUCCESS." << endl;
	    newEvent.exit_now = false;
	    newEvent.try_again = true;
	  }
	  return newEvent;
	  break;
	}  // end of switch

      }  // end of "else" for the if(poll(&ufds, 1, timeout) == 0)
  

      // Ok, so now we have something to read.  Let's read it in!
      rc = readit(sd, buff, MAX_MSG);
      *debug << "DEBUG:  got a packet from the server. rc == " << rc << endl;

      if(rc < 0) {
	*debug << "ERROR:  Can't readit data.  " << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	newEvent.exit_now = true;
	return newEvent;
      }
      else if(rc == 0) {	
	*debug << "ERROR:  Aw fuck.  Got that nasty server spasm of " 
	       << "rc == 0 again." << endl
	       << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
	newEvent.exit_now = true;
	return newEvent;
      }
      
    } // end of:     while(rc == 0)

    recvBuff.clear();  // We shouldn't need to call this, but just in case.
    recvBuff.add(buff, rc);
  }  // end of:   if(recvBuff.size() == 0)
  *debug << "DEBUG:  right before striping" << endl;

  // Strip the header.
  recvBuff.strip();
  *debug << "DEBUG:  got to right before the switch." << endl;


  // Phew, finally got to this point.  Let's analyze this packet.

  
  // The 'event' switch.
  switch(recvBuff.getPacketID()) {
  case SID_CHATEVENT:
    
    *debug << "DEBUG:  just got SID_CHATEVENT.  ";
//     *debug << "This buffer's data:.." << endl;
//     recvBuff.print_buffer();
//     *debug << "DEBUG:  now extracting..." << endl;
    // Format:
    // (u32) Event ID
    // (u32) Flags
    // (u32) Ping
    // (u32) IP Address*
    // (u32) Account number*
    // (u32) Registration authority*
    // (STRING) Username
    // (STRING) Text
    
    // * == defunct.
    u32 defunct_value;  // This will get thrown away.

    recvBuff.extract(newEvent.eventID);
    recvBuff.extract(newEvent.flags);
    recvBuff.extract(newEvent.ping);
    recvBuff.extract(defunct_value);  
    recvBuff.extract(defunct_value);
    recvBuff.extract(defunct_value);
    recvBuff.extract(newEvent.username);
    recvBuff.extract(newEvent.text);
    
    *debug << "DEBUG:  finished doin extracts." << endl;
    break;
  default:
    // Handle a non-chatevent.
    *debug << "DEBUG:  Handling nonchat packet..." << endl;
    if(! handle_nonchat()) {
      disconnectNow();
      newEvent.exit_now = true;
    }
    else {
      // Try to get another event...
      newEvent.exit_now = false;
      newEvent.try_again = true;
      return newEvent;
    }
    break;
  }

  return newEvent;
} // end of    event conGuy::gimmeEvent()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::handle_nonchat()
// 
// Description: 
//   This function handles a packet that is recieved that is not part of
//   the standard chat messages that we want to parse.
// 
//   These are the packet SID's:
//     SID_FLOODDETECTED
//     SID_MESSAGEBOX
//     SID_PING
//     SID_FRIENDLIST
//     SID_FRIENDUPDATE
//     SID_FRIENDADDED
//     SID_FRIENDREMOVED
//     SID_FRIENDMOVED
//     SID_NULL
//
// 
bool conGuy::handle_nonchat() {

  switch(recvBuff.getPacketID()) {


  case SID_FLOODDETECTED:
    // Server will disconnect us right after sending this message.
    conlog << "Flood detected from server." << endl;

    // r_pkt_SID_FLOODDETECTED() does absolutely nothing!!
    //   It's just there for consistency.
    if(! r_pkt_SID_FLOODDETECTED()) {
      *debug << "ERROR:  r_pkt_SID_FLOODDETECTED() failed." << endl;
      conlog << "ERROR:  r_pkt_SID_FLOODDETECTED() failed." << endl;
      return false;
    }
    *debug << "DEBUG:  just got SID_FLOODDETECTED." << endl;
    // false means quit in this case.
    return false;
    break;


  case SID_MESSAGEBOX:

    if(! r_pkt_SID_MESSAGEBOX()) {
      *debug << "ERROR:  r_pkt_SID_MESSAGEBOX() failed." << endl;
      conlog << "ERROR:  r_pkt_SID_MESSAGEBOX() failed." << endl;
      return false;
    }
    *debug << "DEBUG:  just got SID_MESSAGEBOX." << endl;
    return true;
    break;


  case SID_PING: {
    u32 pingVal;

    // For debug timestamp.
    time_t rawtime;
    struct tm * timeinfo;

    if(! r_pkt_SID_PING(pingVal)) {
      *debug << "ERROR:  r_pkt_SID_PING() failed." << endl;
      conlog << "ERROR:  r_pkt_SID_PING() failed." << endl;
      return false;
    }
    // Take a timestamp for the debug output.
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    *debug << "DEBUG:  just got SID_PING.  "
	   << string(asctime(timeinfo)) << endl;

    if(! s_pkt_SID_PING(pingVal)) {
      *debug << "ERROR:  s_pkt_SID_PING() failed." << endl;
      conlog << "ERROR:  s_pkt_SID_PING() failed." << endl;
      return false;
    }
    // Take a timestamp for the debug output.
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    *debug << "DEBUG:  just sent SID_PING.  " 
	   << string(asctime(timeinfo)) << endl;
    
    return true;
  } break;

    // TODO:  These should probably be put in the event switch.
    // Search for "The 'event' switch." to see which one I mean.
  case SID_FRIENDLIST:
  case SID_FRIENDUPDATE:
  case SID_FRIENDADDED:
  case SID_FRIENDREMOVED:
  case SID_FRIENDMOVED:


  case SID_NULL:
  default:
    return trash_curr_packet();
  }  // End of switch(recvBuff.getPacketID())
} // end of   bool conGuy::handle_nonchat()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool conGuy::trash_curr_packet() 
//
// Description:
//   This is a packet we don't care about.  Trash the current stripped 
//   packet in the buffer.  NOT clear the buffer!
//
bool conGuy::trash_curr_packet() {
  
  unsigned short size = recvBuff.getPacketSz();

  *debug << "DEBUG:  About to trash pkt "
	 << print_hex_form(recvBuff.getPacketID()) << ".";
//   *debug << "Here it is before "
// 	 << "stripping and trashing:" << endl;
  
//   *debug << "0xFF "                                   // Initial byte
// 	 << print_hex_form(recvBuff.getPacketID())    // Packet ID
// 	 << print_hex_form( * (((u8*)&size)+0) )  // Size (1st byte)
// 	 << print_hex_form( * (((u8*)&size)+1) ); // Size (2nd byte)
  for(int i = 0; i < size; i++) {
    u8 currByte;
    recvBuff.extract(currByte);
//     *debug << print_hex_form( currByte );
  }
//   *debug << endl;

  *debug << "DEBUG:  Finished trashing packet " 
	 << print_hex_form(recvBuff.getPacketID()) << endl;
  if(recvBuff.getPacketSz() != 0) {
    *debug << "DEBUG:  CRAPpo!  This packet shoulda got trashed, "
	   << "but packetSz isn't 0." << endl
	   << "          " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  return true;
}  // end of   bool conGuy::trash_curr_packet() 




// End of conGuy.cc
